package com.hmall.search.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.query.ItemPageQuery;
import com.hmall.common.domain.PageDTO;
import com.hmall.search.constants.EsConstant;
import com.hmall.search.domain.po.Item;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.service.ISearchService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Api(tags = "搜索相关接口")
@RestController
@RequestMapping("/search")
@RequiredArgsConstructor
public class SearchController {

    private final ISearchService searchService;

    private final RestHighLevelClient restHighLevelClient;

    // 数据库的查
    @ApiOperation("搜索商品")
//    @GetMapping("/list")
    public PageDTO<ItemDTO> search(ItemPageQuery query) {
        // 分页查询
        Page<Item> result = searchService.lambdaQuery()
                .like(StrUtil.isNotBlank(query.getKey()), Item::getName, query.getKey())
                .eq(StrUtil.isNotBlank(query.getBrand()), Item::getBrand, query.getBrand())
                .eq(StrUtil.isNotBlank(query.getCategory()), Item::getCategory, query.getCategory())
                .eq(Item::getStatus, 1)
                .between(query.getMaxPrice() != null, Item::getPrice, query.getMinPrice(), query.getMaxPrice())
                .page(query.toMpPage("update_time", false));
        // 封装并返回
        return PageDTO.of(result, ItemDTO.class);
    }


    // 搜索引擎的查
    @ApiOperation("搜索商品")
    @GetMapping("/list")
    public PageDTO<ItemDTO> searchF(ItemPageQuery query) throws IOException {
        // 构建请求对象
        SearchRequest searchRequest = new SearchRequest(EsConstant.ITEM_INDEX);
        // 构建查询条件
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 存在关键词
        if (StrUtil.isNotBlank(query.getKey())) {
            queryBuilder.must(QueryBuilders.matchQuery(EsConstant.NAME_FIELD, query.getKey()));
        }
        // 存在品牌
        if (StrUtil.isNotBlank(query.getBrand())) {
            queryBuilder.filter(QueryBuilders.termQuery(EsConstant.BRAND_FIELD, query.getBrand()));
        }
        // 存在类目
        if (StrUtil.isNotBlank(query.getCategory())) {
            queryBuilder.filter(QueryBuilders.matchQuery(EsConstant.CATEGORY_FIELD, query.getCategory()));
        }
        // 价格区间
        if (query.getMaxPrice() != null) {
            queryBuilder.filter(QueryBuilders.rangeQuery(EsConstant.PRICE_FIELD).gte(query.getMinPrice()).lte(query.getMaxPrice()));
        }

        // 查询+分页，分页的页码，页面大小本身ItemPageQuery没有但是它继承了PageQuery
        searchRequest.source().query(queryBuilder).size(query.getPageSize()).from((query.getPageNo() - 1) * query.getPageSize());


        // sort
        if (StrUtil.isNotBlank(query.getSortBy())) {
            // 要求的排序字段不为空
            searchRequest.source().sort(query.getSortBy(), query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        } else {
            // 排序字段为空，默认用"update_time"
            searchRequest.source().sort(EsConstant.UPDATETIME_FIELD, query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        }


        // 接收回应
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 获取响应体里面的数据，不是桶，1包2，1就是外面的hits，2分别为数据的总数、真正的数据hits
        SearchHits hits = response.getHits();
        // 一共回了多少数据
        long total = hits.getTotalHits().value;
        // 页码
        long pages = query.getPageNo();
        // 真正的数据，就是一项一项的产品数据
        SearchHit[] searchHits = hits.getHits();
        ArrayList<ItemDTO> itemDocs = new ArrayList<>();
        if (searchHits != null) {
            for (SearchHit hitsHit : searchHits) {
                itemDocs.add(JSONUtil.toBean(hitsHit.getSourceAsString(), ItemDTO.class));
            }
        }
        // 封装并返回
        return new PageDTO<>(total, pages, itemDocs);
    }

    @ApiOperation("分类聚合")
    @PostMapping("/filter")
    public String filter(@RequestBody ItemPageQuery query) throws IOException {
        // 构建请求对象
        SearchRequest searchRequest = new SearchRequest(EsConstant.ITEM_INDEX);
        // 查询关键字
        if (StrUtil.isNotBlank(query.getKey())) {
            searchRequest.source().query(QueryBuilders.matchQuery(EsConstant.NAME_FIELD, query.getKey()));
        }
        // 不用返回数据
        searchRequest.source().size(0);
        /* 开始按照品牌聚合
                聚合名称 brand_agg
                聚合类型 terms
                聚合字段 brand
         */
        searchRequest.source().aggregation(AggregationBuilders.terms(EsConstant.BRAND_AGG_NAME).size(20).field("brand"));

        /* 按照分类聚合
                聚合名称 category_agg
                聚合类型 terms
                聚合字段 category
         */
        searchRequest.source().aggregation(AggregationBuilders.terms(EsConstant.CATEGORY_AGG_NAME).size(20).field("category"));
        // 发送请求
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 解析聚合的结果集
        Aggregations aggregations = response.getAggregations();
        // 根据聚合名字获取结果集
        Terms brand_terms = aggregations.get(EsConstant.BRAND_AGG_NAME);
        Terms category_terms = aggregations.get(EsConstant.CATEGORY_AGG_NAME);
        // 从结果集中获取桶，收集品牌
        List<? extends Terms.Bucket> brand_buckets = brand_terms.getBuckets();
        // 遍历、收集
        ArrayList<String> brand_List = new ArrayList<>();
        for (Terms.Bucket bucket : brand_buckets) {
            // 获取品牌数据
            brand_List.add(bucket.getKeyAsString());
        }
        // 从结果集中获取桶，收集分类
        List<? extends Terms.Bucket> category_buckets = category_terms.getBuckets();
        // 遍历、收集
        ArrayList<String> category_List = new ArrayList<>();
        for (Terms.Bucket bucket : category_buckets) {
            // 获取分类数据
            category_List.add(bucket.getKeyAsString());
        }
        // 封Map
        HashMap<String, List<String>> result = new HashMap<>();
        result.put(EsConstant.CATEGORY_FIELD, category_List);
        result.put(EsConstant.BRAND_FIELD, brand_List);
        // 将map处理为JSON字符串
        return JSONUtil.toJsonStr(result);
    }
}
